package com.du.dupload;

import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class URLdownloadBytes implements Runnable {
	/**
	 * Execution flag
	 */
	private boolean executing = false;

	private String filename;

	/**
	 * Splitter cancelation flag
	 */
	private boolean canceled = false;

	private boolean finished = false;

	private boolean error = false;
	
	private static final int MAX_BUFFER_SIZE = 1024;
	
	private long downloaded; 

	private long size = -1;
	
	private URL url;

	private String name = "";

	private List<String> out;

	private int progress;

	public URLdownloadBytes(String filename) {
		super();
		this.filename = filename;
		this.out = new ArrayList<String>();
		try {
			this.url = new URL(filename);
		} catch (MalformedURLException e) {
			setError(true);
		}
	}

	private synchronized void addOut(String el) {
		out.add(AppConfig.getInstance().getOutDir() + el);
	}

	public synchronized void cancel() {
		this.canceled = true;
	}

	public synchronized void download() throws IllegalStateException {
		while (isExecuting() && isCanceled()) {
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (isExecuting()) {
			return;
		}
		setExecuting(true);
		setCanceled(false);
		setFinished(false);
		setError(false);
		this.out = new ArrayList<String>();
		Thread t = new Thread(this, "URL downl started");
		t.start();
	}
	
	private String getFileName(URL url) {
	    String fileName = url.getFile();
	    return fileName.substring(fileName.lastIndexOf('/') + 1);
	}

	private void downloadProcess() {
		RandomAccessFile file = null;
		InputStream stream = null;

		try {
			// Open connection to URL.
			HttpURLConnection connection = (HttpURLConnection) url
					.openConnection();

			// Specify what portion of file to download.
			connection.setRequestProperty("Range", "bytes=" + downloaded + "-");

			// Connect to server.
			connection.connect();

			// Make sure response code is in the 200 range.
			if (connection.getResponseCode() / 100 != 2) {
				setError(true);
			}

			// Check for valid content length.
			long contentLength = connection.getContentLength();
			if (contentLength < 1) {
				//setError(true);
			}

			/*
			 * Set the size for this download if it hasn't been already set.
			 */
			if (size == -1) {
				size = contentLength;
			}

			// Open file and seek to the end of it.
			file = new RandomAccessFile(AppConfig.getInstance().getOutDir()+getFileName(url), "rw");
			setName(getFileName(url));
			file.seek(downloaded);
			

			stream = connection.getInputStream();
			while (!isCanceled()) {
				/*
				 * Size buffer according to how much of the file is left to
				 * download.
				 */
				byte buffer[];
				if (size - downloaded > MAX_BUFFER_SIZE) {
					buffer = new byte[MAX_BUFFER_SIZE];
				} else {
					buffer = new byte[(int)(size - downloaded)];
				}

				// Read from server into buffer.
				int read = stream.read(buffer);
				if (read == -1)
					break;

				// Write buffer to file.
				file.write(buffer, 0, read);
				downloaded += read;
				setProgress((int) (size/downloaded));
			}

			addOut(filename);
			
		} catch (Exception e) {
			setError(true);
		} finally {
			// Close file.
			if (file != null) {
				try {
					file.close();
				} catch (Exception e) {
				}
			}

			// Close connection to server.
			if (stream != null) {
				try {
					stream.close();
				} catch (Exception e) {
				}
			}
		}
	}

	public synchronized String getName() {
		return name;
	}

	private synchronized void setName(String name) {
		this.name = name;
	}

	public synchronized int getProgress() {
		return progress;
	}

	public synchronized List<String> getResult() {
		if (isFinished()) {
			return new ArrayList<String>(out);
		} else {
			return new ArrayList<String>();
		}
	}

	public synchronized String getSize() {
		return new Long(size).toString();
	}

	public synchronized boolean isCanceled() {
		return canceled;
	}

	public synchronized boolean isError() {
		return error;
	}

	public synchronized boolean isExecuting() {
		return executing;
	}

	public synchronized boolean isFinished() {
		return finished;
	}

	@Override
	public void run() {
		downloadProcess();
		if (!isCanceled()) {
			setFinished(true);
		}
		setExecuting(false);
	}

	private synchronized void setCanceled(boolean canceled) {
		this.canceled = canceled;
	}

	private synchronized void setError(boolean error) {
		this.error = error;
		if (error == true)
			this.canceled = true;
	}

	private synchronized void setExecuting(boolean executing) {
		this.executing = executing;
	}

	private synchronized void setFinished(boolean finished) {
		this.finished = finished;
	}

	private synchronized void setProgress(int progress) {
		this.progress = progress;
	}

	private synchronized void setSize(String size) {
		this.size = Integer.parseInt(size);
	}

}
